/*
 * Copyright (C) 2007,2008  Jonathan Hendler
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 * 
 * The authors can be contacted at: 
 * @author Jonathan Hendler [jonathan DOT hendler AT google DOT com]
 * 
 * @license GPLv2
 */

#include "nymrl_sqlite.h"

//GLOBALS
//http://www.space.unibe.ch/comp_doc/c_manual/C/CONCEPT/storage_class.html
//Global, safe? //SQLite must be compiled with threadsafe and shared connections
static sqlite3 * nymrl_db = NULL;
static sqlite3 * nymrl_log = NULL;


/**
 * SQLite helper functions
 * */

int nymrl_sqlite_global_close(){
	if (nymrl_db){
		NYMRL_SQLITE_CLOSE(nymrl_db);
	}
	if (nymrl_log){
		NYMRL_SQLITE_CLOSE(nymrl_log);
	}
	return 0;
}




sqlite3 * nymrl_get_router_db(){
	char * db_location;
	if (!nymrl_db){
		db_location = nymrl_conf_get_node_db();
		if (!db_location) ny_log_error("node db_location = %s",db_location);
		nymrl_sqlite_open(db_location,&nymrl_db);	
	}
	return nymrl_db;
}

sqlite3 * nymrl_get_log_db(){
	char * db_location;
	if (!nymrl_log){
		db_location = nymrl_conf_get_log_db();
		if (!db_location) ny_log_error(" log db_location = %s",db_location);
		nymrl_sqlite_open(db_location,&nymrl_log);
	}
	return nymrl_log;
}

inline char * nyml_get_config(const char * key){
	const char * temp = "SELECT config_value FROM %s WHERE config_key='%s' ";
	char * sql = nymrl_sqlite_create_sql(temp,NYMRL_ROUTER_CONFIGURATION_TABLE,key);
	return  nymrl_sqlite_get_single_value(nymrl_db,sql);
}


/**
 * todo, generalize this function?
 * */
inline int nyml_set_config(const char * key, char * value){
	//http://www.thescripts.com/forum/thread83939.html
	const char * update = "UPDATE %s SET config_value='%s' WHERE config_key='%s' ";
	const char * insert = "INSERT INTO %s(config_key,config_value) VALUES ('%s','%s') ";
	char * final_sql;
	int rc;
	char * old_value = nyml_get_config(key);
	
	if ((strlen(old_value)>0)){ //key exists
		//do nothing
		if (strcmp(value,old_value)==0){
			free(old_value);
			return SQLITE_OK;
		}
		final_sql = nymrl_sqlite_create_sql(update,NYMRL_ROUTER_CONFIGURATION_TABLE,value,key);
		free(old_value);
	}  else {
		final_sql = nymrl_sqlite_create_sql(insert,NYMRL_ROUTER_CONFIGURATION_TABLE,key,value);
	}
	
	rc = nymrl_sqlite_exec(nymrl_db,final_sql);
	if (rc != SQLITE_OK){
		//log error
	}
	return rc;
}

inline int  nyml_assert_config(const char * key, char * expected_value){
	char * value = nyml_get_config(key);
	if (strcmp(value,expected_value)==0){
		free(value);
		return TRUE;
	} else {
		free(value);
		return FALSE;
	}
}

void _nymrl_db_log(const char * type, char * message){
	const char * insert = "INSERT INTO logs(type,message,time) VALUES ('%s','%s',datetime('now')) ";
	char * final_sql = nymrl_sqlite_create_sql(insert,type,message);
	nymrl_sqlite_exec(nymrl_log,final_sql);
}

/***/
void nymrl_log_error(char * message){
	_nymrl_db_log("ERROR:",message);
}

void nymrl_log_message(char * message){
	_nymrl_db_log("MESSAGE:",message);
}

int nymrl_sqlite_print_all_nymrl_data(){
//	sqlite3 *db = 
//	int x =  sqlite3_table_column_metadata(
//	  sqlite3 *db,                /* Connection handle */
//	  const char *zDbName,        /* Database name or NULL */
//	  const char *zTableName,     /* Table name */
//	  const char *zColumnName,    /* Column name */
//	  char const **pzDataType,    /* OUTPUT: Declared data type */
//	  char const **pzCollSeq,     /* OUTPUT: Collation sequence name */
//	  int *pNotNull,              /* OUTPUT: True if NOT NULL constraint exists */
//	  int *pPrimaryKey,           /* OUTPUT: True if column part of PK */
//	  int *pAutoinc               /* OUTPUT: True if column is auto-increment */
//	);
	//get list of tables
	//select all from tables
	
	nymrl_sqlite_get_results(1,nymrl_log,"SELECT * FROM logs");
	nymrl_sqlite_get_results(1,nymrl_db,"SELECT * FROM nodes");
	nymrl_sqlite_get_results(1,nymrl_db,"SELECT * FROM config");
	return 0;
}

inline int nyml_set_admin_url(char * url){
	return nyml_set_config(NYMRL_ADMIN_HOST_VAR, url);
}

inline char *  nyml_get_admin_url(){
	return nyml_get_config(NYMRL_ADMIN_HOST_VAR);
}

/**
 * api function for recieving special messages from the PHP admin.
 * returns base64 encoded string, maybe JSON
 * */
inline char * nymrl_admin_execute_base_64_query(char * encoded_sql , char * db_name){
	char * return_value;
	
	//decode the encoded sql to a regular char *
	
	//verify the integrety
	
	//verify the permissions
	
	//choose the db
	
	//run the query and capture response
	
	//encode response
	
	//return
	
	return return_value;
}


/**
 * 
 * @param int drop_tables - removed data if restarting
 * 
 * @todo
 * 
 * TABLES
 * meta
 * log
 * nodes
 *		nid
 * 		nurl
 *      gid
 * 		ntype [master|parent|child]
 * 		version 
 * 
 * */
int nyml_setup_self(int drop_tables){
	char * sql;
	int ret;
	// these are static globals, but defining extern was not working, since init doesn't happen at runtime.
	sqlite3 * nymrl_db =  nymrl_get_router_db();
	sqlite3 * nymrl_log = nymrl_get_log_db();
	
	//sprintf(sql,"DROP DATABASE %s",NYMRL_ROUTER_DB);
	//remove all tables
//	sprintf(sql,"CREATE DATABASE IF NOT EXISTS %s",
//					NYMRL_ROUTER_DB_NAME );
//	ret = nymrl_sqlite_exec(db,sql);
	//ret = nymrl_sqlite_exec(db,"USE %s",NYMRL_ROUTER_DB_NAME);
	//ret = nymrl_sqlite_exec(nymrl_db,"DROP TABLE %s.%s",NYMRL_ROUTER_DB_NAME,NYMRL_ROUTER_DB_TABLE_NODES);
	//ret = nymrl_sqlite_exec(nymrl_db,"DROP TABLE %s.%s",NYMRL_ROUTER_DB_NAME,NYMRL_ROUTER_DB_TABLE_METADATA);
	if (drop_tables == 1 ){
		ret = nymrl_sqlite_drop_table(nymrl_db,NYMRL_ROUTER_DB_TABLE_NODES);
		ret = nymrl_sqlite_drop_table(nymrl_log,NYMRL_LOG_DB_TABLE);
		ret = nymrl_sqlite_drop_table(nymrl_db,NYMRL_ROUTER_DB_TABLE_NODES_X_URL);
		ret = nymrl_sqlite_drop_table(nymrl_db,NYMRL_ROUTER_DB_TABLE_URL);
		ret = nymrl_sqlite_drop_table(nymrl_db,NYMRL_ROUTER_CONFIGURATION_TABLE);
	}
	
	//NODES
	//this node is at x url and in x group with x id and is active

	sql = nymrl_sqlite_create_sql("CREATE TABLE IF NOT EXISTS %s (nid  INTEGER PRIMARY KEY, label VARCHAR(256) , nymrl_version INTEGER, active BINARY   ) ",NYMRL_ROUTER_DB_TABLE_NODES);
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
	sql = nymrl_sqlite_create_sql("CREATE INDEX IF NOT EXISTS nid_lookup ON %s  (nid,active,active) ",NYMRL_ROUTER_DB_TABLE_NODES);
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
	sql = nymrl_sqlite_create_sql("CREATE TABLE IF NOT EXISTS %s  (url VARCHAR(256)  PRIMARY KEY,label VARCHAR(256)  ,active BINARY, max INTEGER, lat TEXT, long TEXT, x INTEGER, y INTEGER, z INTEGER) ",NYMRL_ROUTER_DB_TABLE_URL);
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
	sql = nymrl_sqlite_create_sql("CREATE INDEX IF NOT EXISTS url_lookup ON %s  (url,active)",NYMRL_ROUTER_DB_TABLE_URL);
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
    //#define NYMRL_ROUTER_DB_TABLE_URL "url"
	sql = nymrl_sqlite_create_sql("CREATE TABLE IF NOT EXISTS %s (nid  INTEGER, rel VARCHAR(256) , url VARCHAR(256) ,  active BINARY   ) ",
				NYMRL_ROUTER_DB_TABLE_NODES_X_URL);  
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
	//create node table indexes
	//http://www.sqlite.org/lang_createindex.html
	sql = nymrl_sqlite_create_sql("CREATE INDEX IF NOT EXISTS node_lookup ON %s  (nid,rel,active) ",
			NYMRL_ROUTER_DB_TABLE_NODES_X_URL);//NYMRL_ROUTER_DB_NAME
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
	sql = nymrl_sqlite_create_sql("CREATE INDEX IF NOT EXISTS node_lookup_2 ON %s  (rel,nid,active) ",
				NYMRL_ROUTER_DB_TABLE_NODES_X_URL);//NYMRL_ROUTER_DB_NAME
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
	/////////////////////////////////
	//CONFIGURATION
	sql = nymrl_sqlite_create_sql("CREATE TABLE IF NOT EXISTS %s ( `config_key` VARCHAR(256) PRIMARY KEY, `config_value`  VARCHAR(256) ) ",
						NYMRL_ROUTER_CONFIGURATION_TABLE);  
	ret = nymrl_sqlite_exec(nymrl_db,sql);
	
	if (ret != SQLITE_OK){
		printf("FAILED TO AMKE CONFIG TABLE\n");
		
	}
	
	/////////////////////////////////
	//LOGGING
	sql = nymrl_sqlite_create_sql("CREATE TABLE IF NOT EXISTS %s (type VARCHAR(32), message TEXT, time  VARCHAR(64) ) ",
				NYMRL_LOG_DB_TABLE);  
	ret = nymrl_sqlite_exec(nymrl_log,sql);
	
	return 0;
}

inline NYMRL_URL nymrl_get_self( char * rel){
	return nyml_get_config(rel);
}

/**
 * called from admin
 * */
inline int nymrl_set_self(char * rel, NYMRL_URL this_url){
	return nyml_set_config(rel, this_url); 
}


//////////////////
// SQLite functions
/////////////////
inline  NYMRL_ID _nyml_create_node(NYMRL_ID id,char * label){
	//get last insert id
	if (nymrl_db == NULL) nymrl_db = nymrl_get_router_db();
	const char * temp = "INSERT INTO %s(nid,label,active) VALUES(%lu,'%s',1) ";
	char * sql = nymrl_sqlite_create_sql(temp,NYMRL_ROUTER_DB_TABLE_NODES,id,label);
	nymrl_sqlite_exec(nymrl_db,sql);
	return  id;
}

inline  int _nyml_add_node(NYMRL_ID id, NYMRL_URL url, char * rel){
	if (nymrl_db == NULL) nymrl_db = nymrl_get_router_db();
	const char * temp = "INSERT INTO %s(nid,url,rel) VALUES(%lu,'%s','%s') ";
	char * final_sql = nymrl_sqlite_create_sql(temp,NYMRL_ROUTER_DB_TABLE_NODES_X_URL,id,url,rel);
	return nymrl_sqlite_exec(nymrl_db,final_sql);
}

inline  int _nyml_remove_node(NYMRL_ID nid){
	if (nymrl_db == NULL) nymrl_db = nymrl_get_router_db();
	const char * temp  = "DELETE FROM %s WHERE  nid=%lu ";
	char * final_sql = nymrl_sqlite_create_sql(temp,NYMRL_ROUTER_DB_TABLE_NODES_X_URL,nid);
	nymrl_sqlite_exec(nymrl_db,final_sql);
	final_sql = nymrl_sqlite_create_sql(temp,NYMRL_ROUTER_DB_TABLE_NODES,nid);
	return nymrl_sqlite_exec(nymrl_db,final_sql);
}

inline  int _nyml_update_node(NYMRL_ID id, NYMRL_URL url, char * rel){
	return TRUE;
}

/*** 
 * do not incloude this URL
 * */
inline NYMRL_URL_LIST _nymrl_get_urls(int url_count, NYMRL_ID nid, char * rel, int * row_count){
	if (nymrl_db == NULL) nymrl_db = nymrl_get_router_db();
	sqlite3 * db = nymrl_db;
	
	//buffers 
	char rel_temp[256];
	static char ** return_one;
	int  choice,size;
	NYMRL_URL self =  nymrl_get_self(rel);
	
	if ((rel == NULL) || (strlen(rel) <1)){
		rel_temp[0] = '\0';
	} else {
		sprintf(rel_temp," AND rel='%s' ",rel);
	}
	
	const char * temp = "SELECT url FROM %s WHERE url !='%s'  AND nid=%lu %s ; ";
	char * sql = nymrl_sqlite_create_sql(temp,NYMRL_ROUTER_DB_TABLE_NODES_X_URL,self,nid,rel_temp);
	
	NYMRL_SQLITE_GET_RESULTS;
	
	//int array_size = NYMRL_COUNT(result);
	//if (url_count > 1) printf("\n\t\t\t\tarray_size %d\n",array_size);
	*row_count = nrows;
	
	if ( (result) && url_count == 1){
		//chose randome one	
		 choice = rand() % (nrows);
		 size = strlen(result[choice]) ;
		 *return_one  = (char *) malloc(size*sizeof(char));
		 printf("size %d",size);
		 strncpy(*return_one,result[choice],size);
		 //return_one[0][size] = '\0';
		 //free other results
		 NYMRL_SQLITE_RESULT_CLEANUP;
		 
		 return return_one;
	}  
	
	
	
	return result;
}

